/* Copyright (c) 2022 渝州大数据实验室
 *
 * Lanius is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

package org.yzbdl.lanius.orchestrate.filter;


import cn.hutool.json.JSONUtil;
import org.yzbdl.lanius.orchestrate.config.WhiteLists;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import org.yzbdl.lanius.orchestrate.serv.dto.system.PayLoadClaimDto;
import org.yzbdl.lanius.orchestrate.serv.entity.system.OrgEntity;
import org.yzbdl.lanius.orchestrate.serv.service.system.ManagerService;
import org.yzbdl.lanius.orchestrate.serv.service.system.OrgService;
import org.yzbdl.lanius.orchestrate.serv.service.system.UserService;
import org.yzbdl.lanius.orchestrate.serv.utils.CurrentUserUtil;
import org.yzbdl.lanius.orchestrate.common.exception.runtime.InvalidTokenException;
import org.yzbdl.lanius.orchestrate.common.jwt.JwtHelper;
import org.yzbdl.lanius.orchestrate.common.utils.MessageUtil;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;


/**
 * @author chenjunhao@yzbdl.ac.cn
 */
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    private final UserService userService;
    private final ManagerService managerService;
    private final OrgService orgService;


    public JwtAuthenticationTokenFilter(UserService userService, ManagerService managerService, OrgService orgService) {
        this.userService = userService;
        this.managerService = managerService;
        this.orgService = orgService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
        String authToken = JwtHelper.getTokenFromRequest(request);
        if(!StringUtils.hasText(authToken)){
            throwRequestErrMessage(request, MessageUtil.get("visit.filter.no_token"));
        }
        Map<String,Object> claims = isManagerUrl(request.getRequestURI())?
                JwtHelper.getManagerClaimMapFromToken(authToken):
                JwtHelper.getNormalClaimMapFromToken(authToken);
        if(claims==null){
            throwRequestErrMessage(request, MessageUtil.get("visit.filter.error_token"));
        }
        PayLoadClaimDto payLoadClaimDto = JSONUtil.toBean(JSONUtil.toJsonStr(claims),PayLoadClaimDto.class);
        UserDetails userDetails;
        if(payLoadClaimDto.isManager()){
            userDetails = this.managerService.getById(payLoadClaimDto.getUserId());
        }else{
            OrgEntity orgEntity = orgService.getById(payLoadClaimDto.getOrgId());
            if(orgEntity.getFrozen()){
                throwRequestErrMessage(request, MessageUtil.get("system.org.disable"));
            }
            userDetails = this.userService.getById(payLoadClaimDto.getUserId());
            if(userDetails==null){
                throwRequestErrMessage(request, MessageUtil.get("visit.filter.error_token"));
            }
            if(!userDetails.isEnabled()){
                throwRequestErrMessage(request, MessageUtil.get("system.user.disable"));
            }
            if(!orgService.isUserInOrg(payLoadClaimDto.getUserId(),payLoadClaimDto.getOrgId())){
                throwRequestErrMessage(request, MessageUtil.get("system.user.user_org_not_match"));
            }
        }
        CurrentUserUtil.setCurrentUser(payLoadClaimDto,userDetails);
        chain.doFilter(request, response);
    }


    private void throwRequestErrMessage(HttpServletRequest request, String message){
        request.setAttribute("errMessage",message);
        throw new InvalidTokenException("");
    }

//    private void jumpToError(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
//        request.getRequestDispatcher("/error/invalidToken").forward(request,response);
//    }


    private boolean inMatchUrl(String requestUrl,String[] ignoreUrls) {
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        for (String ignoreUrl : ignoreUrls) {
            if(antPathMatcher.match(ignoreUrl, requestUrl)){
                return true;
            }
        }
        return false;
    }

    /**
     * 判断是否是管理员路由
     * @param requestUrl 访问路由
     * @return 是否
     */
    private boolean isManagerUrl(String requestUrl) {
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        return Arrays.stream(WhiteLists.ONLY_MANAGER).anyMatch(ignoreUrl->antPathMatcher.match(ignoreUrl, requestUrl));
    }

}
